/*
 * TMap Designer - A Thematic Map design software
 * Copyright (C) 2006 World Health Organization
 * http://www.who.int
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */
package who.tmap.toolkit.impl;

import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;

import org.apache.log4j.Logger;

import who.tmap.bo.Attrs;
import who.tmap.bo.BaseMap;
import who.tmap.bo.BizObjects;
import who.tmap.bo.impl.BizObjectFactory;
import who.tmap.common.TMapException;
import who.tmap.toolkit.EditMode;
import who.tmap.toolkit.MappingToolKit;
import who.tmap.toolkit.shape.LabelShape;
import who.tmap.toolkit.shape.MapShape;
import who.tmap.toolkit.shape.MapShapes;
import who.tmap.toolkit.shape.PolygonShape;
import who.tmap.toolkit.shape.PolygonShapeDisputedBorder;
import who.tmap.toolkit.shape.annotation.AnnotationShape;
import who.tmap.toolkit.shape.annotation.BackgroundImageShape;
import who.tmap.toolkit.shape.annotation.LabelsListShape;
import who.tmap.toolkit.shape.annotation.TitleShape;
import who.tmap.toolkit.shape.cdpointsymbol.CDPointSymbol;
import who.tmap.toolkit.shape.impl.MapShapeFactory;

/**
 * A helper dedicated to shape manipulation
 * @see MappingToolKit
 * @author stl
 *
 */
public class MappingToolKitShapeHelper {

    private static Logger logger = Logger.getLogger(MappingToolKitShapeHelper.class);	
	
	private MappingToolKit mtk;
	private MapShapeFactory mapShapeFactory =MapShapeFactory.getInstance();
	private AttrHelper attrHelper =new AttrHelper();
	private CartographicDisplayHelper cdHelper =new CartographicDisplayHelper();

	public MappingToolKitShapeHelper( MappingToolKit mtk ) {
		this.mtk =mtk;
	}
	
	private void setDirty() {
		mtk.getThematicMap().setDirty( true );
	}
	
	private boolean shapeSelectableInTheCurrentEditMode( MapShape shape ) {
		boolean selectable =false;
		
		if ( ( ( mtk.getEditMode() & EditMode.BASE_MAP ) == EditMode.BASE_MAP ) 
			&& ( shape instanceof PolygonShape ) ) {
			selectable =true;
		}
		
		if ( ( ( mtk.getEditMode() & EditMode.DATA ) == EditMode.DATA ) 
				&& ( shape instanceof CDPointSymbol ) ) {
				selectable =true;
			}		
		
		if ( ( ( mtk.getEditMode() & EditMode.ANNOTATIONS ) == EditMode.ANNOTATIONS ) 
			&& ( shape instanceof AnnotationShape ) ) {
			selectable =true;
		}
		
		return selectable;
	}
	
	/**
	 * 	If there are several shapes at this point (eg a polygon and its label)
	 *	get the smaller one
	 */
	public MapShape getShapeAt( Point2D pt ) {
		MapShape shapeAt =null;
		List<MapShape> shapesAt =new ArrayList<MapShape>();
		
		for( MapShape shape: mtk.getShapes() ) {
			if ( shape.contains( pt ) ) {
				boolean visible =true;
				
				// TODO: issue here when the annotations rule stops the
				// label from being displayed...
				// >> The user click does not select the shape !!
				if ( shape instanceof LabelShape ) {
					visible =((LabelShape)shape).isVisible() 
						&& mtk.isAnnotationsVisible() 
						&& mtk.getThematicMap().getAnnotations().isLabelsVisible()
						&& ((LabelShape)shape).getLabelsList() == null;
				}
				
				if ( shape instanceof TitleShape ) {
					visible =((TitleShape)shape).isVisible() 
						&& mtk.isAnnotationsVisible()
						&& mtk.getThematicMap().getAnnotations().isTitleVisible();
				}				
					
				if ( visible && ( shapeSelectableInTheCurrentEditMode( shape ) ) ) {
					shapesAt.add( shape );
				}
			}
		}
		
		if ( shapesAt.size() > 1 ) {
/*			Rectangle2D bounds0 =shapesAt.get( 0 ).getBounds();
			Rectangle2D bounds1 =shapesAt.get( 1 ).getBounds();

			if ( bounds0.contains( bounds1 ) ) {
				shapeAt =shapesAt.get( 1 );
			} else if ( bounds1.contains( bounds0 ) ) {
				shapeAt =shapesAt.get( 0 );
			} else {
				shapeAt =shapesAt.get( 0 );
			}*/
			
			for( int shapeCt =0; shapeCt < shapesAt.size(); shapeCt++ ) {
				MapShape aShape =shapesAt.get( shapeCt );
				
				if ( shapeAt == null || computeSurface( aShape) < computeSurface( shapeAt ) ) {
					shapeAt =aShape;
				}
			}
		} else if ( shapesAt.size() == 1 ) {
			shapeAt =shapesAt.get( 0 );
		}
		
		return shapeAt;
	}	
	
	private double computeSurface( MapShape mapShape ) {
		double surface =0;
		
		if ( mapShape != null && mapShape.getBounds() != null ) {
			surface =mapShape.getBounds().getWidth() * mapShape.getBounds().getHeight();
		}
		
		return surface;
	}
	
	public MapShape selectShapeAt( Point2D pt ) {
		MapShape shape =getShapeAt( pt ); 
		if ( shape != null ) {
			shape.select( !shape.isSelected() );
			
			if ( shape.isSelected() ) {
				mtk.getSelectedShapes().add( shape );
			} else {
				mtk.getSelectedShapes().remove( shape );
			}
		}	
		
		return shape;
	}	
	
	public void selectShape( MapShape mapShape ) {
		mtk.unselectShapes();
		mapShape.select( true );
		mtk.getSelectedShapes().add( mapShape );
	}
	
	public void selectShapes( List<MapShape> mapShapes ) {
		mtk.unselectShapes();
		
		mtk.getSelectedShapes().addAll( mapShapes );
		for( MapShape shape : mapShapes ) {
			shape.select( true );
		}
	}
		
	public MapShape[] getShapesAt( Point2D p1, Point2D p2 ) {
		java.util.List<MapShape> theShapes =new ArrayList<MapShape>(); 
		
		Rectangle2D rect =new Rectangle2D.Double();
		rect.setFrameFromDiagonal( p1, p2 );
		logger.debug( "p1=(" + p1.getX() + ", " + p1.getY() 
		        + ") : p2=(" + p2.getX() + ", " + p2.getY() + ")" );
		
		for( MapShape shape: mtk.getShapes() ) {
			//logger.debug( "checking " + shape );
			
			if ( shape.isInside( rect ) && shapeSelectableInTheCurrentEditMode( shape ) ) {
				theShapes.add( shape );
				//logger.debug( "found " + shape );
			}
		}			
		
		MapShape[] shapesArray =new MapShape[ theShapes.size() ];
		theShapes.toArray( shapesArray );
		
		return shapesArray;
	}	
	
	public void selectShapesAt( Point2D p1, Point2D p2 ) {
		MapShape[] selShapes =getShapesAt( p1, p2 ); 
		if ( selShapes != null ) {
			for( int ct =0; ct < selShapes.length; ct++ ) {
				selShapes[ ct ].select( true );
				mtk.getSelectedShapes().add( selShapes[ ct ] );
			}
		}	
	}
	
	public MapShape getHandleAt( Point2D p ) {
		MapShape selShape =null;
		int handle =-1;
		
		for( MapShape shape : mtk.getSelectedShapes() ) {
			
			if ( shapeSelectableInTheCurrentEditMode( shape ) ) {
				handle =shape.getHandleIndexAt( p );
				
				if ( handle != -1 ) {
					selShape =shape;
					shape.setSelectedHandleIndex( handle );
					break;
				}
			}
		}	
		
		return selShape;
	}
	
	public MapShape addShape( int shape ) throws TMapException {
		mtk.setEditedShape( mapShapeFactory.getGraphic( shape ) );	
		
		mtk.getShapes().add( mtk.getEditedShape() );
		
		// Add a label shape for every polygonShape
		if ( mtk.getEditedShape() instanceof PolygonShape ) {
			PolygonShape polygon =(PolygonShape)mtk.getEditedShape();
			
			LabelShape labelShape =(LabelShape)mapShapeFactory.getGraphic( MapShapes.LABEL );
			labelShape.setMapShape( mtk.getEditedShape() );
			mtk.getShapes().add( labelShape );
			
			polygon.setLabelShape( labelShape );
			
		}
		
		return mtk.getEditedShape();	
	}
	
	public void addShapes( List<MapShape> shapeList ) throws TMapException {

		for( MapShape mapShape : shapeList ) {

			if ( mapShape instanceof BackgroundImageShape ) {
				mtk.setBackgroundImage( (BackgroundImageShape)mapShape );
			} else {
				mtk.getShapes().add( mapShape );
			}
			
			if ( mapShape instanceof PolygonShape ) {
				mtk.getShapes().add( ((PolygonShape)mapShape).getLabelShape() );
			}
		}
	}
	
	@SuppressWarnings("unchecked")
	public void removeShape( MapShape shape ) {
		if ( shape instanceof PolygonShape ) {
			// Remove associated label
			mtk.getShapes().remove( ((PolygonShape)shape).getLabelShape() ); 
			mtk.getSelectedShapes().remove( ((PolygonShape)shape).getLabelShape() );
		} else if ( shape instanceof LabelsListShape ) {
			// Reset LabelShape labelsList for the deleted labelsList
			for( MapShape mapShape : mtk.getShapes() ) {
				if ( mapShape instanceof LabelShape ) {
					LabelShape labelShape =(LabelShape)mapShape;
					if ( labelShape.getLabelsList() == shape ) {
						labelShape.setLabelsList( null );
					}
				}
			}
		} else if ( shape instanceof CDPointSymbol ) {
			CDPointSymbol symbol =(CDPointSymbol)shape;
			Attrs data =mtk.getThematicMap().getData();

			List rowsToDelete =new ArrayList();
			List<List> rows =data.getRows();
			for( List row : rows ) {
				if ( ( (Double)row.get( data.getXColumnIdx() ) == symbol.getPt().getX() )
				  && ( (Double)row.get( data.getYColumnIdx() ) == -symbol.getPt().getY() ) ) {
					rowsToDelete.add( row );						
				}
			}	
			
			rows.removeAll( rowsToDelete );
		}
		
		// Don't delete label shapes, just make them invisible
		if ( shape instanceof LabelShape ) {
			((LabelShape)shape).setVisible( false );
		} else {
			mtk.getShapes().remove( shape );
		}
		
		mtk.getSelectedShapes().remove( shape );

	}
	
	public void unselectShapes() {
		for( MapShape shape : mtk.getSelectedShapes() ) {			
			shape.select( false );
		}		
		
		mtk.getSelectedShapes().clear();

	}
	
	@SuppressWarnings("unchecked")
	public void moveSelectedShapes( Point2D draggingPt ) {
		if ( !mtk.getSelectedShapes().isEmpty() ) {
			
			for( MapShape shape : mtk.getSelectedShapes() ) {

				Point2D scaledAnchor =mtk.convertScreenToMap( mtk.getAnchor() );
				Point2D scaledDrag =mtk.convertScreenToMap( draggingPt ); 

				CDPointSymbol symbol =null;
				Point2D origSymbolPt =null;
				if ( shape instanceof CDPointSymbol ) {
					symbol =(CDPointSymbol)shape;
					origSymbolPt =(Point2D)symbol.getPt().clone();
				}
				
				shape.moveShape( scaledAnchor, scaledDrag ); 

				// Update the matching coordinates in the attributes
				// for Point CD
				if ( shape instanceof CDPointSymbol ) {
					Attrs data =mtk.getThematicMap().getData();

					List<List> rows =data.getRows();
					for( List row : rows ) {
						if ( ( (Double)row.get( data.getXColumnIdx() ) == origSymbolPt.getX() )
						  && ( (Double)row.get( data.getYColumnIdx() ) == -origSymbolPt.getY() ) ) {
							row.set( data.getXColumnIdx(), symbol.getPt().getX() );
							row.set( data.getYColumnIdx(), -symbol.getPt().getY() );							
						}
					}
				}				
			}	
		}	
		
		mtk.setAnchor( draggingPt );
	}	
	
/*	@SuppressWarnings("unchecked")
	public Rectangle2D moveSelectedShapesTest( Point2D draggingPt ) {
		Rectangle2D rect =null;
		if ( !mtk.getSelectedShapes().isEmpty() ) {
			
			for( MapShape shape : mtk.getSelectedShapes() ) {

				Point2D scaledAnchor =mtk.convertScreenToMap( mtk.getAnchor() );
				Point2D scaledDrag =mtk.convertScreenToMap( draggingPt ); 
				
				if ( rect == null ) {
					rect =(Rectangle2D)shape.getBounds().clone();
				} else {
					rect.add( (Rectangle2D)shape.getBounds() );
				}

				CDPointSymbol symbol =null;
				Point2D origSymbolPt =null;
				if ( shape instanceof CDPointSymbol ) {
					symbol =(CDPointSymbol)shape;
					origSymbolPt =(Point2D)symbol.getPt().clone();
				}
				
				//shape.moveShape( scaledAnchor, scaledDrag ); 
				
				if ( shape instanceof AnnotationShape ) {
					AnnotationShape annShape =(AnnotationShape)shape;
					rect.add( annShape.moveShapeTest( scaledAnchor, scaledDrag ) ); 
				} else {
					shape.moveShape( scaledAnchor, scaledDrag ); 
				}
				
				// Update the matching coordinates in the attributes
				// for Point CD
				if ( shape instanceof CDPointSymbol ) {
					Attrs data =mtk.getThematicMap().getData();

					List<List> rows =data.getRows();
					for( List row : rows ) {
						if ( ( (Double)row.get( data.getXColumnIdx() ) == origSymbolPt.getX() )
						  && ( (Double)row.get( data.getYColumnIdx() ) == -origSymbolPt.getY() ) ) {
							row.set( data.getXColumnIdx(), symbol.getPt().getX() );
							row.set( data.getYColumnIdx(), -symbol.getPt().getY() );							
						}
					}
				}				
			}	
		}	
		
		mtk.setAnchor( draggingPt );
		
		return rect;
	}*/
	
	/*
	 * Returns true if a CDPointSymbol was deleted
	 */
	public boolean deleteSelectedShapes() {
		boolean cdPointSymbolDeleted =false;
		if ( !mtk.getSelectedShapes().isEmpty() ) {
			
			while( !mtk.getSelectedShapes().isEmpty() ) {
				if ( mtk.getSelectedShapes().get(0) instanceof CDPointSymbol ) {
					cdPointSymbolDeleted =true;
				}
				removeShape( mtk.getSelectedShapes().get(0) );
			}
			
			setDirty();
		}	
		
		return cdPointSymbolDeleted;
	}
	
	public MapShape mergeSelectedPolygons() throws TMapException {
		MapShape masterShape =null;
		
		if ( mtk.getSelectedShapes().size() == 2 ) {
			
			PolygonShape pol =(PolygonShape)mtk.getSelectedShapes().get( 0 );
			try {
				masterShape =(MapShape)pol.deepClone();
			} catch (CloneNotSupportedException e) {
				e.printStackTrace();
			}
			
			((PolygonShape)masterShape).merge( (PolygonShape)mtk.getSelectedShapes().get( 1 ) );
			
			mtk.removeShape( (PolygonShape)mtk.getSelectedShapes().get( 0 ) );	
			// One shape left in the selectedShapes so its index becomes now 0 !
			mtk.removeShape( (PolygonShape)mtk.getSelectedShapes().get( 0 ) );
			unselectShapes();
			
			List<MapShape> mapShapes =new ArrayList<MapShape>();
			mapShapes.add( masterShape );
			addShapes( mapShapes );
			selectShapes( mapShapes );
		}	
		
		return masterShape;
	}
	
	public List<MapShape> splitPolygon( PolygonShape polygon, Point2D firstPt, Point2D secondPt ) throws TMapException {
	    PolygonShape[] polygons =polygon.split( firstPt, secondPt );
	    int polygonIdx =mtk.getShapes().indexOf( polygon );
	    LabelShape labelShape =polygon.getLabelShape();
	    
	    // Remove original polygon
	    mtk.getShapes().remove( polygon );
		mtk.getSelectedShapes().remove( polygon );
		
		for( int ct =0; ct < polygons.length; ct++ ) {
			mtk.getShapes().add( polygonIdx, polygons[ ct ] );

			LabelShape ls =(LabelShape)mapShapeFactory.getGraphic( MapShapes.LABEL );
			ls.setVisible( labelShape.isVisible() );
			ls.setMapShape( polygons[ ct ] );
			
			polygons[ ct ].setLabelShape( ls );
			
			mtk.getShapes().add( ls );			
		}
		
		// Remove original polygon's label
		mtk.getShapes().remove( polygon.getLabelShape() );  		
		mtk.getSelectedShapes().remove( polygon.getLabelShape() );	
		
		setDirty();
		
		List<MapShape> pols =new ArrayList<MapShape>();
		pols.add( polygons[ 0 ] );
		pols.add( polygons[ 1 ] );
		
		return pols;
	}
	
	public Map<String,PolygonShape> getPolygonsForSelectionByName() {
	    // Build a sorted collection of polygons
	    Map<String, PolygonShape> polygons =new TreeMap<String, PolygonShape>();
	    int ct =0;
	    for( MapShape shape : mtk.getShapes()  ) {	        
	        if ( shape instanceof PolygonShape 
	        		&& ((PolygonShape)shape).getName() != null 
	        		&& !"".equals( ((PolygonShape)shape).getName() ) ) {
	        	polygons.put( ((PolygonShape)shape).getName() + "_" + ct++, (PolygonShape)shape );
	        }
	    };	
	    
	    return polygons;
	}
	
	public void selectPolygonsSelectedByName( List<String> selectedPolygons ) {
	    if ( selectedPolygons != null ) {
	        unselectShapes();
	        
	        for( String polygonName : selectedPolygons ) {
	            
	        	for( MapShape shape : mtk.getShapes() ) {
	    	        
	    	        if ( shape instanceof PolygonShape && 
	    	        		polygonName.equalsIgnoreCase( ((PolygonShape)shape).getName() ) ) { 
	    	            shape.select( true );
	    	            mtk.getSelectedShapes().add( shape );
	    	        }
	    	    };		
	            
	        }
	    }	   
	}
	
	@SuppressWarnings("unchecked")
	public List<String> getNonMatchingData() {
		
		Set<String> nmd =new TreeSet<String>( String.CASE_INSENSITIVE_ORDER );
		
	    Map<String, Integer> polygons =new TreeMap<String, Integer>();

	    for( MapShape shape : mtk.getShapes()  ) {	        
	        if ( shape instanceof PolygonShape 
	        		&& ((PolygonShape)shape).getName() != null 
	        		&& !"".equals( ((PolygonShape)shape).getName() ) ) {
	        	polygons.put( ((PolygonShape)shape).getName().toUpperCase(), 1 );
	        }
	    };	
	    
	    Attrs attrs =mtk.getThematicMap().getData();
		List rows =(List)attrs.getRows();

		for( int ct =0; ct < rows.size(); ct++ ) {
			List row =(List)rows.get( ct );

			Object keyObj =row.get( attrs.getJoinIdIdx() );
			if ( keyObj != null ) {
				String key =row.get( attrs.getJoinIdIdx() ).toString();
				if ( polygons.get( key.toUpperCase() ) == null ) {
					nmd.add( key );
				}
			}
		}	
		
		return new ArrayList<String>( nmd );
	}

	public Map<String, PolygonShape> getOrphanPolygons() throws TMapException {
		Map<String, PolygonShape> orphans =new TreeMap<String, PolygonShape>( String.CASE_INSENSITIVE_ORDER );
		
		Map<MapShape, Object> assignedVal =cdHelper.assignValues( mtk
																, mtk.getThematicMap().getData()
																, mtk.getThematicMap().getCartographicDisplay()
																, false );

		HashMap<String, Integer> siblings =new HashMap<String, Integer>();
		for( MapShape polygon : assignedVal.keySet() ) {
			siblings.put( ((PolygonShape)polygon).getName(), 1 );
		}
		
		for( MapShape shape : mtk.getShapes() ) {
			if ( shape instanceof PolygonShape ) {
				String name =((PolygonShape)shape).getName();
				
				if ( siblings.get( name ) == null ) {
					orphans.put( name, ((PolygonShape)shape) );
				}
			}
		}
		
		return orphans;
	}	
	
/*	public Point2D insertPolygonPoint( Point2D pt ) {
	    boolean firstFound =false;
	    unselectShapes();
	    
	    for( MapShape shape : mtk.getShapes() ) {
			
		    if ( shape instanceof PolygonShape ) {			
		        Point2D matchingPt =((PolygonShape)shape).insertPoint( pt, firstFound );
		        
		        if ( matchingPt != null ) {
		        	mtk.getSelectedShapes().add( shape );
		            shape.select( true );
		            
		            if ( !firstFound ) {
		                firstFound =true;
		                pt.setLocation( matchingPt.getX(), matchingPt.getY() );
		            }
		        }
		        
		        setDirty();
		    }
		}
		
		return pt;
	}*/
	
	public void addDisputedBorder( Point2D pt ) {
	    boolean firstFound =false;
	    unselectShapes();
	    
	    List<MapShape> dbShapes =new ArrayList<MapShape>();
	    
	    for( MapShape shape : mtk.getShapes() ) {
			
		    if ( shape instanceof PolygonShape ) {			
		        if ( ((PolygonShape)shape).addDisputedBorder( pt, 0 ) ) {
		        	mtk.getSelectedShapes().add( shape );
		            shape.select( true );
		            dbShapes.add( shape );
			        
			        setDirty();
		        }
		    }
		}
	    
	    // Make sure disputed borders are drawn in the same direction
	    // because of stroke issue
	    if ( dbShapes.size() > 1 ) {
	    	List<PolygonShapeDisputedBorder> dbs0 =((PolygonShape)dbShapes.get(0)).getDisputedBorders();
	    	for( int ct =1; ct < dbShapes.size(); ct++ ) {
	    		PolygonShape dbShape =(PolygonShape)dbShapes.get( ct );
	    		for( PolygonShapeDisputedBorder psdb : dbShape.getDisputedBorders() ) {
	    			for( PolygonShapeDisputedBorder db0 : dbs0 ) {
	    				if ( psdb.getLine().getBounds2D().equals( db0.getLine().getBounds2D() ) ) {
	    					psdb.getLine().setLine( db0.getLine().getX1(), db0.getLine().getY1(), 
	    											db0.getLine().getX2(), db0.getLine().getY2() );
	    				}
	    			}
	    		}
	    	}
	    }

	}	
	
	public boolean anyShapeSelected() {
		return !mtk.getSelectedShapes().isEmpty();
	}
	
	public void closePolygon() {
		if ( mtk.getEditedShape() instanceof PolygonShape ) {
			((PolygonShape)mtk.getEditedShape()).close();
			
			mtk.setAnchor( null );
			mtk.setDrag( null );
			
			setDirty();
		}
	}		
	
	public BaseMap retrieveBaseMap() throws TMapException {
		BaseMap baseMap =(BaseMap)BizObjectFactory.getInstance().get( BizObjects.BASE_MAP );

		baseMap.setFile( null );
		baseMap.setMaxX( mtk.getMaxX() );
		baseMap.setMaxY( mtk.getMaxY() );
		baseMap.setMinX( mtk.getMinX() );
		baseMap.setMinY( mtk.getMinY() );
		
		baseMap.setShapes( mtk.getShapes() );
		
		baseMap.setDbaseJoinIdx( mtk.getDbaseJoinIdx() );
		
		return baseMap;
	}	
	
	@SuppressWarnings("unchecked")
	public List addPointEntry( Point2D pt, String description ) {
		Attrs attrs =mtk.getThematicMap().getData();
		List<List> rows =attrs.getRows();
		
		List row =new ArrayList();
		rows.add( row );
		
		for( int ct =0; ct < attrs.getHeader().size(); ct++ ) {
			String type =attrs.getHeader().get( ct ).getType();
			
			if ( ct == attrs.getXColumnIdx() ) {
				row.add( attrHelper.getValueFromString( Double.toString( pt.getX() ), type ) );
			} else if ( ct == attrs.getYColumnIdx() ) {
				row.add( attrHelper.getValueFromString( Double.toString( -pt.getY() ), type ) );
			} else if ( ct == attrs.getDescriptorColumnIdx() ) {
				row.add( description );				
			} else {
				row.add( attrHelper.getValueFromString( type.equals( String.class.getName() ) ? "" : "0", type ) );
			}
		}
		
		setDirty();
		
		return row;
	}	
	
	public void selectCDPointsByDesc( List<Point2D> cdPointsDesc ) {
	    if ( cdPointsDesc != null ) {
	        unselectShapes();
	            
        	for( MapShape shape : mtk.getShapes() ) {
    	        
    	        if ( shape instanceof CDPointSymbol && 
    	        		cdPointsDesc.contains( ((CDPointSymbol)shape).getPt() ) ) { 
    	            shape.select( true );
    	            mtk.getSelectedShapes().add( shape );
    	        }
    	    };		

	    }			
	}

}
